#/usr/bin/python27
#-*- coding: utf-8 -*-
#  Copyright 2011-2013 wuweihui@cn.ibm.com
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import re
import os

class simpledict(dict):
   """
   Dict with ordered keys, the key ordered as the input sequence
   """
   def __init__(self, dictstr=None):
      dict.__init__(self)
      self._keys = []
      if dictstr:
         self._init_items(dictstr.strip())
   
   def _init_items(self, dictstr):
      if not dictstr.startswith('{') and dictstr.endswith('}'):
         raise RuntimeError("Dict format error!")
      k, p, vs = dictstr[1:-1].partition(':')
      while p:
         vs = vs.strip()
         v = self._read_dict_value(vs)
         if vs.find(',', len(v)) != -1:
            vs = vs[vs.find(',', len(v))+1:].strip()
         else:
            vs = vs[len(v):].strip()
         if v.startswith('{'):
            v = 'simpledict("""' + v + '""")'
         self.__setitem__(eval(k.strip()), eval(v.strip()))
         k, p, vs = vs.partition(':')
      
   def _read_dict_value(self, dictstr):
      countermap = {'[': ']',
                    '{': '}',
                    '(': ')'}
      if dictstr.startswith('\"\"\"') or dictstr.startswith("\'\'\'"):
         ret = re.match(r"\"\"\".*?\"\"\"|\'\'\'.*?\'\'\'", dictstr)
         return ret.group()
      if dictstr[0] in ["\'", "\""]:
         ret = re.match(r"\".*?\"|\'.*?\'", dictstr)
         return ret.group()
      if dictstr[0] in countermap.keys():
         counter = 1
         i = 1
         while counter > 0 and i < len(dictstr):
            if dictstr[i] == dictstr[0]:
               counter += 1
            if dictstr[i] == countermap[dictstr[0]]:
               counter -= 1
            i += 1
         if counter > 0:
            raise RuntimeError("Dict format error!")
         return dictstr[:i]
      else:
         i = 0
         while i < len(dictstr):
            if dictstr[i] == ',':
               break
            i += 1
         return dictstr[:i]
      
   def __setitem__(self, i, v):
      dict.__setitem__(self, i, v)
      if i not in self._keys:
         self._keys.append(i)
   
   def keys(self):
      return self._keys
   
   def items(self):
      return [(k, self[k]) for k in self._keys]
   
   def values(self):
      return [self[k] for k in self._keys]
   
   def update(self, dc):
      for k, v in dc.items():
         self[k] = v
   
   def pop(self, key):
      dict.pop(self, key)
      self._keys.remove(key)
   
   def insert(self, i, k, v):
      dict.__setitem__(self, k, v)
      if k not in self._keys:
         self._keys.insert(i, k)

class LogBase(object):
   def __init__(self, output, *args):
      self._output = output
   
   def __str__(self):
      return self._output
         
   def __repr__(self):
      return self.__str__()
      
   def __unicode__(self):
      return self.__str__()
   
   def __hasattr__(self, name):
      try:
         self.__getattribute__(name)
         return 1
      except:
         return 0
      
   def __getattrnames__(self):
      return [name for name in dir(self) if not name.startswith('_')]
   
   def _merge(self, obj):
      for objattr in obj.__getattrnames__():
         if self.__hasattr__(objattr):
            if self.__getattribute__(objattr) != obj.__getattribute__(objattr):
               raise RuntimeError("Cannot merge this two object, unmatched duplicate attribute found!\n%s\n%s" % (self, obj))
         else:
            self.__setattr__(objattr, obj.__getattribute__(objattr))
      self._output += '\n' + obj._output
   
class LogListBase(list):
   def __init__(self, output, *args):
      list.__init__(self, *args)
      self._output = output
      
   def Update(self, itemlist, key='ObjectId', replace=False):
       if not isinstance(itemlist, list):
          itemlist = [itemlist]
       for item in itemlist:
           updateflag = False
           if not item.__hasattr__(key):
               continue
           for i in range(len(self)):
               msitem = self[i]
               if msitem.__hasattr__(key) \
               and msitem.__getattribute__(key) == item.__getattribute__(key):
                  self[i] = item
                  updateflag = True
                  break
           if not updateflag:
               self.append(item)
   
   def GetItemList(self, key, value=None, inst=None):
       itemlist = LogListBase('')
       for item in self:
           if item.__hasattr__(key) \
           and (value == None or item.__getattribute__(key) == value) \
           and (inst == None or item._instname == inst):
              itemlist.append(item)
              
       return itemlist
    
   def RemoveByKey(self, key, value, inst=None):
      for item in self.GetItemList(key, value, inst):
         self.remove(item)
         
   def Copy(self):
      return type(self)(self._output)
   
   def Filter(self, key=None, value=None):
      itemlist = LogListBase('')
      for item in self:
         if not key and value in item._output:
            itemlist.append(item)
         if key and item.__hasattr__(key) and value in item.__getattribute__(key):
            itemlist.append(item)
            
      return itemlist

class eMSLogItem(LogBase):
   """
   """
   def __init__(self, output, instname, *args):
      LogBase.__init__(self, output, *args)
      self._instname = instname
      for line in output.splitlines():
         self._process_line(line)
   
   def _process_line(self, line):
      _pattern = re.compile(r"""(\w+)\s                          #type
                                (\w+)\[*\]*\=                    #attribute name
                                (.*)(\s\[\w+\])*                 #value
                                """, re.X)
      found = re.match(_pattern, line)
      if not found == None:
         ret = found.groups()
         self.__setattr__(ret[1], ret[2])
         #self[ret[1]] = ret[2]
         
   def __getkeyvalue__(self):
      return (self.__hasattr__("ObjectId") and self.__getattribute__("ObjectId")) \
             or (self.__hasattr__("Child") and self.__getattribute__("Child")) \
             or (self.__hasattr__("DiscoveryId") and self.__getattribute__("DiscoveryId"))
   
def get_emslogitem_instances(data):
   _re_pattern = re.compile(r"\=+ Instance \d+ of (\w+)(\r\n\r\n|\n\n)(.*?)(\r\n\r\n|\n\n)", re.DOTALL)
   return [eMSLogItem(output[2], output[0]) for output in re.findall(_re_pattern, data)]

class eMSLogItemList(LogListBase):
   _re_pattern = re.compile(r"\=+ Instance \d+ of (\w+)(\r\n\r\n|\n\n)(.*?)(\r\n\r\n|\n\n)", re.DOTALL)
   def __init__(self, output, *args):
      LogListBase.__init__(self, output, *args)
      self._process_output(output)

   def _process_output(self, output):
      for itemout in re.findall(self._re_pattern, output):
         self.append(eMSLogItem(itemout[2], itemout[0]))
         
      
class cMSLogItem(LogBase):
   """
   """
   def __init__(self, output, *args):
      LogBase.__init__(self, output, *args)
      self._process_lines(output.splitlines())
      
   def _process_lines(self, lines):
      if lines[0].endswith(';'):
         self._cmd = lines[0].split(';')[0].strip()
         lines = lines[1:]
      else:
         self._cmd = None
      _pattern = re.compile(r"""(\w+)\s                             #type
                                (\w+)\[*\]*\=                       #attribute name
                                (.*?)(\s\[\w+\])*$                  #value
                                """, re.X)
      _parameter = re.compile(r"""([\w\s]+)                         #name
                                  \s*\=\s*(.*?)\,*$                 #value
                                  """, re.X)
      for line in lines:
         found = re.match(_pattern, line)
         parfound = re.match(_parameter, line)
         if found != None:
            ret = found.groups()
            attrname = ret[1]
            self.__setattr__(attrname, ret[2])
         elif parfound != None:
            ret = parfound.groups()
            attrname = ret[0].strip().replace(' ', '_')
            self.__setattr__(attrname, ret[1])
         if line.strip().startswith('<') and self.__getattribute__(attrname).startswith('<'):
            #print line
            self.__setattr__(attrname, self.__getattribute__(attrname) + os.linesep + line)
            
class cMSDiscoverItem(cMSLogItem):
   """
   """
   def __init__(self, output, *args):
      cMSLogItem.__init__(self, output, *args)
      self.res = self.id = None
      self._get_res_and_id(output)
      
   def _get_res_and_id(self, output):
      _pattern = re.compile(r"started with res (\d+)\; id \{([\w\-]+)\}")
      
      ret = _pattern.search(output)
      if ret:
         self.res, self.id = ret.groups()

      
class cMSDiscoverStatusItem(LogBase):
   """
   """
   def __init__(self, output, *args):
      LogBase.__init__(self, output, *args)
      self.id = self.completion = self.status = self.msg = None
      self._process(output)
      
   def _process(self, output):
      output = output.replace('\n', '')
      _pattern = re.compile(r"discovery \{([\w\-]+)\} (\w+)\; status \[(\d+)/(.*?)\]")
      
      ret = _pattern.search(output)
      if ret:
         self.id, self.completion, self.status, self.msg = ret.groups()
   
class eMSSizeInfoItem(cMSLogItem):
   """
   """
   def __init__(self, output, *args):
      cMSLogItem.__init__(self, output, *args)
      for line in output.splitlines():
         self._process_line(line)
   
   def _process_line(self, line):
      _pattern = re.compile(r"""(\w+)\s+:\s+(.*)""")
      found = re.match(_pattern, line)
      if not found == None:
         ret = found.groups()
         self.__setattr__(ret[0], ret[1])
         
class eMSStgGrpItem(cMSLogItem):
   """
   """
   def __init__(self, output, *args):
      cMSLogItem.__init__(self, output, *args)
      self._process(output)
   
   def _process(self, output):
      _pattern = re.compile(r"""(\w+)\s+\-\>\s+(.*?)(\n\n|\r\n\r\n)""", re.DOTALL)
      attrs = _pattern.findall(output)
      for attr in attrs:
         self.__setattr__(attr[0], self._read_values(attr[1]))
   
   def _read_values(self, out):
      _pattern = re.compile(r"\s*(\w+):\s+(.*)")
      values = []
      for line in out.splitlines():
         ret = _pattern.match(line)
         if ret:
            k, v = ret.groups()
            values.append(v)
      return values
   
class eCIMLogItem(LogBase):
   """
path= IBMTSDS_SEVolume.CreationClassName="IBMTSDS_SEVolume",DeviceID="IBM.2810-1300207-100548",SystemCreationClassName="IBMTSDS_StorageSystem",SystemName="IBM.2810-1300207"

//Instance of IBMTSDS_SEVolume
instance of IBMTSDS_SEVolume
{

};
   """
   _re_pattern = re.compile(r"path\s*\=\s*([^\s]+)\s+//Instance of \w+\s+instance of\s+(\w+)\s+\{.*?\};", re.DOTALL)
   
   def __init__(self, output, instname, path, *args):
      LogBase.__init__(self, output, *args)
      self._instname = instname
      self.path = path
      #one defect in cim when processing a value with space, use self._process to by pass this problem
#      for line in output.splitlines():
#         ret = self._process_line(line)
#         if not ret and line.match(r'\w+\s*\='):
#            print "*WARN* Log Parse Failure at %s\n\t%s" % (self.path, line)
      self._process()
   
   def _process_line(self, line):
      _pattern = re.compile(r"""(\w+)\s*\=\s*               #attribute name
                                (.*?)                       #value
                                ;$""", re.X)
      found = re.match(_pattern, line)
      if not found == None:
         ret = found.groups()
         v = ret[1]
         if v.startswith('"'):
            v = v[1:-1]
         self.__setattr__(ret[0], v)
         return True
      return False

   def _process(self):
      _pattern = re.compile(r"""(\w+)\s*\=\s*               #attribute name
                                (.*?)                       #value
                                ;""", re.X|re.DOTALL)
      for attrname, value in _pattern.findall(self._output):
         if value.startswith('"'):
            value = value[1:-1]
         value = value.replace('\n    ', '')
         self.__setattr__(attrname, value)
         
   def __getkeyvalue__(self):
      return self.path
   
class eCIMLogItemList(LogListBase):
   def __init__(self, output, *args):
      LogListBase.__init__(self, output, *args)
      self._process_output(output)

   def _process_output(self, output):
      re_path = re.compile(r"path\s*\=\s*(.*)")
      re_inst_start = re.compile(r"instance of (\w+)")
      re_inst_end = re.compile(r"\}\;$")
      inst_flag = False
      inst_lines = []
      inst_name = ''
      inst_path = ''
      for line in output.splitlines():
         ret = re_path.match(line)
         if ret:
            inst_path = ret.group(1)
            
         ret = re_inst_start.match(line)
         if ret:            
            inst_flag = True
            inst_name = ret.group(1)
                 
         ret = re_inst_end.match(line)
         if ret:
            inst_flag = False
            if inst_path and inst_name:
               self.append(eCIMLogItem('\n'.join(inst_lines), inst_name, inst_path))
            else:
               raise RuntimeError("eCIMLogItem parse error!\n%s" % '\n'.join(inst_lines))
            inst_lines = []
            inst_path = ''
            inst_name = ''
         
         if inst_flag:
            inst_lines.append(line)
     

def get_attr_from_value(value):
   """
   String ObjectId=root/ibm:h9.186.96.201:5989;IBMTSDS_FCPort.CreationClassName=%'IBMTSDS_FCPort%',DeviceID=%'100210%',SystemCreationClassName=%'IBMTSDS_StorageSystem%',SystemName=%'IBM.2810-1300207%'
   String __PATH=IBMTSDS_StorageSystem.CreationClassName="IBMTSDS_StorageSystem",Name="IBM.2810-1300207"
   return a dict value, key = attrname, value = attrvalue
   """
   attrdict = {}
   re_attr = re.compile(r'(\w+)\=(\"(.*?)\"|\%\'(.*?)\%\')')
   ret = re_attr.findall(value)
   for attrname, _, v1, v2 in ret:
      attrdict[attrname] = v1 or v2
   return attrdict


if __name__ == '__main__':
   pass
